using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Comteck.Dto;
using Comteck.Dto.Common;

namespace Comteck.Winforms.Validations {
  /// <summary>
  /// DataAnnotation帮助类
  /// </summary>
  public static class DataAnnotationHelper {

    /// <summary>
    /// 直接验证结果
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    /// <param name="entity"></param>
    /// <returns></returns>
    public static List<ValidationResult> Validate<TEntity>(TEntity entity) {
      var context = new ValidationContext(entity, null, null);

      var results = new List<ValidationResult>();
      Validator.TryValidateObject(entity, context, results, true);

      return results;
    }

    /// <summary>
    /// 获取实体对象的基于DataAnnotation的验证规则
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    /// <param name="entity"></param>
    /// <param name="deeploop"></param>
    /// <returns></returns>
    public static List<MvcValidationRule> GetValidationRules<TEntity>(TEntity entity) {
      var rules = new List<MvcValidationRule>();

      // 查找对象
      var descriptor = GetTypeDescriptor(typeof(TEntity));

      // 查找所有属性
      foreach (PropertyDescriptor propertyDescriptor in descriptor.GetProperties()) {
        // 查找验证属性
        var validateAttrList = propertyDescriptor.Attributes.OfType<ValidationAttribute>();

        if (validateAttrList.IsNullOrEmpty()) {
          continue;
        }

        // 查找字段名称
        var displayName = GetDisplayName(propertyDescriptor);

        foreach (var validationAttribute in validateAttrList) {
          MvcValidationRule rule = null;

          if (validationAttribute is RequiredAttribute required) {
            rule = new MvcValidationRule() {
              Field = propertyDescriptor.Name,
              FieldType = propertyDescriptor.PropertyType,
              DisplayName = displayName,
              ErrorMessage = validationAttribute.FormatErrorMessage(displayName),
              ValidationType = MvcValidationType.RequiredAttribute,
              Order = 1,
            };
          } else if (validationAttribute is MaxLengthAttribute maxLength) {
            rule = new MvcValidationRule() {
              Field = propertyDescriptor.Name,
              FieldType = propertyDescriptor.PropertyType,
              DisplayName = displayName,
              MaxLength = maxLength.Length,
              ErrorMessage = validationAttribute.FormatErrorMessage(displayName),
              ValidationType = MvcValidationType.MaxLengthAttribute,
              Order = 2,
            };
          } else if (validationAttribute is MinLengthAttribute minLength) {
            rule = new MvcValidationRule() {
              Field = propertyDescriptor.Name,
              FieldType = propertyDescriptor.PropertyType,
              DisplayName = displayName,
              MinLength = minLength.Length,
              ErrorMessage = validationAttribute.FormatErrorMessage(displayName),
              ValidationType = MvcValidationType.MinLengthAttribute,
              Order = 2
            };
          } else if (validationAttribute is MinValueAttribute minValue) {
            rule = new MvcValidationRule() {
              Field = propertyDescriptor.Name,
              FieldType = propertyDescriptor.PropertyType,
              DisplayName = displayName,
              MinValue = minValue.MinValue,
              ErrorMessage = validationAttribute.FormatErrorMessage(displayName),
              ValidationType = MvcValidationType.MinValueAttribute,
              Order = 3
            };
          } else if (validationAttribute is MaxValueAttribute maxValue) {
            rule = new MvcValidationRule() {
              Field = propertyDescriptor.Name,
              FieldType = propertyDescriptor.PropertyType,
              DisplayName = displayName,
              MaxValue = maxValue.MaxValue,
              ErrorMessage = validationAttribute.FormatErrorMessage(displayName),
              ValidationType = MvcValidationType.MaxValueAttribute,
              Order = 4
            };
          }
          //else if (validationAttribute is CompareAttribute compareAttribute) {
          //  rule = new MvcValidationRule() {
          //    Field = propertyDescriptor.Name,
          //    FieldType = propertyDescriptor.PropertyType,
          //    DisplayName = displayName,
          //    OtherProperty = compareAttribute.OtherProperty,
          //    OtherPropertyDisplayName = compareAttribute.OtherPropertyDisplayName,
          //    ErrorMessage = validationAttribute.FormatErrorMessage(displayName),
          //    ValidationType = MvcValidationType.CompareAttribute,
          //    Order = 3
          //  };
          //}

          if (rule != null) {
            rules.Add(rule);
          }
        }
      }

      return rules;
    }

    /// <summary>
    /// 查找属性名称
    /// </summary>
    /// <param name="property"></param>
    /// <returns></returns>
    private static string GetDisplayName(PropertyDescriptor property) {
      // 先查找 DisplayAttribute
      var name = property.Attributes.OfType<DisplayAttribute>().FirstOrDefault()?.Name;

      if (name.IsNullOrEmpty()) {
        name = property.DisplayName;
      }

      if (name.IsNullOrEmpty()) {
        name = property.Name;
      }

      return name;
    }

    public static List<MvcValidationRule> IsValid<T>(this T o, bool only1Level = false) {
      return IsValid(typeof(T), o, only1Level);
    }

    private static List<MvcValidationRule> IsValid(Type t, object o, bool only1Level) {
      List<MvcValidationRule> errors = new List<MvcValidationRule>();

      var descriptor = GetTypeDescriptor(t);

      foreach (PropertyDescriptor propertyDescriptor in descriptor.GetProperties()) {
        foreach (var validationAttribute in propertyDescriptor.Attributes.OfType<ValidationAttribute>()) {
          if (!validationAttribute.IsValid(propertyDescriptor.GetValue(o))) {
            MvcValidationRule error = new MvcValidationRule();
            error.Field = propertyDescriptor.Name;
            error.ErrorMessage = validationAttribute.FormatErrorMessage(propertyDescriptor.Name);
            errors.Add(error);
          }
        }
      }

      if (!only1Level) {
        if (o.GetType().IsClass && !o.GetType().Equals(typeof(string))) {
          foreach (var p in o.GetType().GetProperties()) {
            object pValue = p.GetValue(o, null);
            if (pValue != null) {
              List<MvcValidationRule> pErrors = IsValid(p.PropertyType, pValue, only1Level);
              errors.AddRange(pErrors);
            }
          }
        }
      }

      return errors;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    private static ICustomTypeDescriptor GetTypeDescriptor(Type type) {
      return new AssociatedMetadataTypeTypeDescriptionProvider(type).GetTypeDescriptor(type);
    }

  }

}
